/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_ContainerBase_h__
#define __dylab_ContainerBase_h__

//===========================================================================================
#include <dylab/utils/thread/Thread.h>

//===========================================================================================
namespace dylab {

    /** 
     * Thread-safe container base class based on STL-containers. 
     * Only the modification of the container is guaranteed to be thread-safe, 
     * but not the modification of the container itself!
     */   
    template <
        class _STLcontainerT
    >
    class ContainerBase
    {      
        // all of these classes are defined below
        friend class IteratorBase;
        friend class ConstIteratorBase;
        friend class ReaderBase;
        friend class WriterBase;

    protected:
        typedef ContainerBase<_STLcontainerT> ContainerBaseT;
    private:  
        /// iterator template implements common iterator functionalitys
        template <
            class _ContainerBaseT,
            class _IteratorT,
            class _STLcontainerT
        >
        class _IteratorTpl
        {
        private:
            _ContainerBaseT * mContainerBase;
            _STLcontainerT * mSTLcontainer;
            _IteratorT mCurrent;
            bool mSuspended;
        public:
            _IteratorTpl(const _IteratorTpl & _iter)
                : mContainerBase(_iter.mContainerBase), mSTLcontainer(_iter.mSTLcontainer), mCurrent(_iter.mCurrent)
                , mSuspended(false)
                    { mContainerBase->_sharedLock(); }
            _IteratorTpl(_ContainerBaseT & _c)
                : mContainerBase(&_c), mSuspended(false)
                    { 
                        mSTLcontainer = mContainerBase->_sharedLock();
                        mCurrent = mSTLcontainer->begin();
                    }
            virtual ~_IteratorTpl()
                    { mContainerBase->_sharedUnlock(); }
            bool isValid() const
                    { return mCurrent != mSTLcontainer->end(); }
            void reset()
                    { mCurrent = mSTLcontainer->begin(); }
            _IteratorTpl & operator++ ()
                { ++mCurrent; return *this; }
            _IteratorTpl & operator++ (int)
                { mCurrent++; return *this; }
        protected:
            _IteratorT & getCurrent()
                    { 
                        DYLAB_ASSERT(!mSuspended);
                        return mCurrent;
                    }
            const _IteratorT & getCurrent() const
                    { 
                        DYLAB_ASSERT(!mSuspended);
                        return mCurrent;
                    }
        };
        //-----------------------------------------------------------------------------
    protected:
        /// utility base class (RIAA) for walking through all container elements
        class IteratorBase
            : public _IteratorTpl<ContainerBaseT, typename _STLcontainerT::iterator, _STLcontainerT>
        {
        private:
            typedef _IteratorTpl<ContainerBaseT, typename _STLcontainerT::iterator, _STLcontainerT> IteratorTplT;
        public:
            IteratorBase(const IteratorBase & _iter)
                : IteratorTplT(_iter)
                    { }
            IteratorBase(ContainerBaseT & _c)
                : IteratorTplT(_c)
                    { }
        };
        //-----------------------------------------------------------------------------
    protected:
        /// utility base class (RIAA) for walking through all container elements, without the 
        /// permission to modify them
        class ConstIteratorBase
            : public _IteratorTpl<const ContainerBaseT, typename _STLcontainerT::const_iterator, const _STLcontainerT>
        {
        private:
            typedef _IteratorTpl<const ContainerBaseT, typename _STLcontainerT::const_iterator, const _STLcontainerT> IteratorTplT;
        public:
            ConstIteratorBase(const ConstIteratorBase & _iter)
                : IteratorTplT(_iter)
                    { }
            ConstIteratorBase(const ContainerBaseT & _c)                     
                : IteratorTplT(_c)
                    { }                    
        };
        //-----------------------------------------------------------------------------
    protected:
        /// reader base class (RIAA)
        class ReaderBase
        {
        protected:
            // dylab & stl container pointers
            const ContainerBaseT * mContainerBase;
            const _STLcontainerT * mSTLcontainer;
        public:
            ReaderBase(const ReaderBase & _reader)
                : mContainerBase(_reader.mContainerBase), mSTLcontainer(_reader.mContainerBase->_sharedLock())
                    { }
            ReaderBase(const ContainerBaseT & _c)
                : mContainerBase(&_c), mSTLcontainer(_c._sharedLock())
                    { }
            virtual ~ReaderBase()
                    { mContainerBase->_sharedUnlock(); }
            uint32_t getCount() const
                    { return static_cast<uint32_t>(mSTLcontainer->size()); } 
        };
        //-----------------------------------------------------------------------------
    protected:
        /// writer base class (RIAA)
        class WriterBase
        {
        protected:
            // dylab & stl container pointers
            ContainerBaseT * mContainerBase;
            _STLcontainerT * mSTLcontainer;
        public:
            WriterBase(ContainerBaseT & _c)
                : mContainerBase(&_c), mSTLcontainer(_c._exclusiveLock())
                    { }
            virtual ~WriterBase()
                    { mContainerBase->_exclusiveUnlock(); }
        };
        //-----------------------------------------------------------------------------

    private:
        /// the structure itself
        _STLcontainerT mSTLcontainer;
        ///
        mutable Thread::CriticalSection mCS;

    public:

        /** Default constructor.
         */
        ContainerBase()
                { }

        /** Copy constructor.
         */
        ContainerBase(const ContainerBase & c)
            : mSTLcontainer(c.mSTLcontainer)
                { }

        /** Destructor.
         */           
        virtual ~ContainerBase()
                { }

        /**
         * Returns the getCount of all elements in the container
         */
        uint32_t getCount() const
                { return ReaderBase(*this).getCount(); }

    private:

        /** 
         * @return  Returns the STL-structure pointer.
         */
        _STLcontainerT * _sharedLock()
                { mCS.shLock(); return &mSTLcontainer; }

        /** 
         * @return  Returns the STL-structure pointer.
         */
        const _STLcontainerT * _sharedLock() const
                { mCS.shLock(); return &mSTLcontainer; }

        /** 
         * Unlocks container shared lock. 
         */
        void _sharedUnlock() const
                { mCS.shUnlock(); }

        /** 
         * @return  Returns the STL-structure pointer.
         */
        _STLcontainerT * _exclusiveLock()
                { mCS.exLock(); return &mSTLcontainer; }

        /** 
         * Unlocks container iterator. 
         */
        void _exclusiveUnlock()
                { mCS.exUnlock(); }
    };    
}
//===========================================================================================
#endif // __dylab_ContainerBase_h__
